Maps (Hash Maps)

  • Maps .

  • Zero value of a map is nil . A nil  map has no keys.

Maps Memory Layout

  • Always a copy

    m: map[string]int = ... 
    m2 := m 
    // points to the same data as m
    
    m2["foo"] = 123 
    // m is not aware of the new key that was added--it's in the data, but m has the wrong length 
    // worse, this could cause the map to reallocate, in which case m would point to freed memory 
    
    delete(m2) 
    // m is now definitely invalid
    
  • Are you trying to remove the entire map entry? if so: https://pkg.odin-lang.org/base/builtin/#delete_key  and then delete  the deleted_key  (and deleted_entry  if you allocated it) (edited)

  • Be consistent with your keys in the map--like I said, clone them all (and then you know you should delete them all when you delete the map) or don't clone any (and then you know not to delete any, but you also need to be careful with what you insert).

  • string s just happen to be particularly annoying to deal with because  they're pointers

  • Allocator requirements :

    • Ginger Bill:

      • So the map  type in Odin REQUIRES an allocator that can do 64-byte aligned allocations.

      • What you'll need to do is change the alignment when initializing the dynamic arena: dynamic_arena_init(&arena, alignment=64)

      • This does mean every allocation is a bit wasteful, unfortunately.

      • But that's the problem of custom allocators and trying to treat them "generally" any way.

Create

  • Using make :

    • Uses the current context .

    m := make(map[string]int)
    
  • Map literals:

    m := map[string]int{
        "Bob" = 2,
        "Chloe" = 5,
    }
    

Delete

  • Using delete :

    delete(m)
    

Insert / update

m[key] = elem

Access

elem = m[key]
  • If an element for a key does not exist, the zero  value of the element will be returned.

elem, ok := m[key] // `ok` is true if the element for that key exists
    // β€œcomma ok idiom”

//or 

ok := key in m     // `ok` is true if the element for that key exists

Remove element

delete_key :: proc(m: ^$T/map[$K]$V, key: $K) -> (deleted_key: $K, deleted_value: $V) {…}

Modify

Test :: struct {
    x: int,
    y: int,
}

m := map[string]Test{
    "Bob" = { 0, 0 },
    "Chloe" = { 1, 1 },
}

// Method 1
value, ok := &m["Bob"]
if ok {
    value^ = { 2, 2 }
}

// Method 2
m["Bob"] = { 3, 3 }

// Method 3 (Forbidden)
m["Chloe"].x = 0

"Compound Literals"

  • To enable compound literals for map s, #+feature dynamic-literals  must be enabled per file.

  • This is because dynamic literals will use the current context.allocator  and thus implicitly allocate.

  • The opt-in feature exists so that Odin does not implicitly allocate by default and give the user any surprises.

Container Calls

  • The built-in map also supports all the standard container calls that can be found with the dynamic array .

  • len(some_map)

    • Returns the number of slots used

  • clear(&some_map)

    • Clears the entire map - dynamically allocated content needs to be freed manually

  • cap(some_map)

    • Returns the capacity of the map - the map will reallocate when exceeded

  • shrink(&some_map)

    • Shrinks the capacity down to the current length

  • reserve(&some_map, capacity)

    • Reserves the requested number of elements